#include "include/receive_sharing_intent/receive_sharing_intent_plugin.h"

// This must be included before many other Windows headers.
#include <windows.h>

// For getPlatformVersion; remove unless needed for your plugin implementation.
#include <VersionHelpers.h>

#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>

#include <map>
#include <memory>
#include <sstream>

namespace {
	using flutter::EncodableList;
	using flutter::EncodableMap;
	using flutter::EncodableValue;
	const char kChannelName[] = "receive_sharing_intent/messages";
	// Converts the given UTF-16 string to UTF-8.
	std::string Utf8FromUtf16(const std::wstring& utf16_string) {
		if (utf16_string.empty()) {
			return std::string();
		}
		int target_length = ::WideCharToMultiByte(
			CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(),
			static_cast<int>(utf16_string.length()), nullptr, 0, nullptr, nullptr);
		if (target_length == 0) {
			return std::string();
		}
		std::string utf8_string;
		utf8_string.resize(target_length);
		int converted_length = ::WideCharToMultiByte(
			CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(),
			static_cast<int>(utf16_string.length()), utf8_string.data(),
			target_length, nullptr, nullptr);
		if (converted_length == 0) {
			return std::string();
		}
		return utf8_string;
	}

	// Converts the given UTF-8 string to UTF-16.
	std::wstring Utf16FromUtf8(const std::string& utf8_string) {
		if (utf8_string.empty()) {
			return std::wstring();
		}
		int target_length =
			::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(),
				static_cast<int>(utf8_string.length()), nullptr, 0);
		if (target_length == 0) {
			return std::wstring();
		}
		std::wstring utf16_string;
		utf16_string.resize(target_length);
		int converted_length =
			::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(),
				static_cast<int>(utf8_string.length()),
				utf16_string.data(), target_length);
		if (converted_length == 0) {
			return std::wstring();
		}
		return utf16_string;
	}

	class ReceiveSharingIntentPlugin : public flutter::Plugin {
	public:
		static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);

		virtual ~ReceiveSharingIntentPlugin();

	private:
		ReceiveSharingIntentPlugin(flutter::PluginRegistrarWindows* registrar);
		// Called when a method is called on this plugin's channel from Dart.
		void HandleMethodCall(
			const flutter::MethodCall<flutter::EncodableValue>& method_call,
			std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
		// The registrar for this plugin, for accessing the window.
		flutter::PluginRegistrarWindows* registrar_;
	};
	// static
	void ReceiveSharingIntentPlugin::RegisterWithRegistrar(
		flutter::PluginRegistrarWindows* registrar) {
		auto channel =
			std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
				registrar->messenger(), kChannelName,
				&flutter::StandardMethodCodec::GetInstance());

		// auto plugin = std::make_unique<ReceiveSharingIntentPlugin>(registrar);
		// Uses new instead of make_unique due to private constructor.
		std::unique_ptr<ReceiveSharingIntentPlugin> plugin(new ReceiveSharingIntentPlugin(registrar));

		channel->SetMethodCallHandler(
			[plugin_pointer = plugin.get()](const auto& call, auto result) {
			plugin_pointer->HandleMethodCall(call, std::move(result));
		});

		registrar->AddPlugin(std::move(plugin));
	}

	ReceiveSharingIntentPlugin::ReceiveSharingIntentPlugin(flutter::PluginRegistrarWindows* registrar): registrar_(registrar) {}

	ReceiveSharingIntentPlugin::~ReceiveSharingIntentPlugin() = default;

	void ReceiveSharingIntentPlugin::HandleMethodCall(
		const flutter::MethodCall<flutter::EncodableValue>& method_call,
		std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
		if (method_call.method_name().compare("getPlatformVersion") == 0) {
			std::ostringstream version_stream;
			version_stream << "Windows ";
			if (IsWindows10OrGreater()) {
				version_stream << "10+";
			}
			else if (IsWindows8OrGreater()) {
				version_stream << "8";
			}
			else if (IsWindows7OrGreater()) {
				version_stream << "7";
			}
			result->Success(flutter::EncodableValue(version_stream.str()));
		} else if (method_call.method_name().compare("readClipboard") == 0) {
			// HGLOBAL   hglb;
			// LPTSTR    lptstr;
			//if (!IsClipboardFormatAvailable(CF_TEXT))
			//    return;
			if (!OpenClipboard(registrar_->GetView()->GetNativeWindow()))
				return;
			flutter::EncodableList paths;
			UINT uFormat = EnumClipboardFormats(0);
			while (uFormat) {
				bool a = uFormat == CF_UNICODETEXT;
				printf("%d", a);
				if (IsClipboardFormatAvailable(uFormat)) {
					HANDLE hData = GetClipboardData(uFormat);
					if (hData != NULL) {
						if (uFormat == CF_UNICODETEXT) {
							WCHAR* pszText = static_cast<WCHAR*>(GlobalLock(hData));
							if (pszText != nullptr) {
								std::wstring text(pszText);
								paths.push_back(flutter::EncodableValue(Utf8FromUtf16(text)));
							}
							GlobalUnlock(hData);
						}
						if (uFormat == CF_TEXT) {
							char* pszText = static_cast<char*>(GlobalLock(hData));
							if (pszText != nullptr) {
								std::string text(pszText);
								paths.push_back(flutter::EncodableValue(text));
							}
							GlobalUnlock(hData);
						}

						if (uFormat == CF_HDROP) {
							HDROP hDrop = (HDROP)GlobalLock(hData);
							if (hDrop) {
								UINT fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, 0, 0);
								UINT filenameLength;
								TCHAR   lpszFileName[256];
								for (UINT i = 0; i < fileCount; ++i) {
									filenameLength = DragQueryFile(hDrop, i, 0, 0);
									DragQueryFile(hDrop, i, lpszFileName, filenameLength + 1);
									paths.push_back(flutter::EncodableValue("file://" + Utf8FromUtf16(lpszFileName)));
								}
							}
							GlobalUnlock(hData);
						}

						if (uFormat == CF_ENHMETAFILE) {
							char* pszText = static_cast<char*>(GlobalLock((HENHMETAFILE)hData));
							if (pszText != nullptr) {
								std::string text(pszText);
								paths.push_back(flutter::EncodableValue(text));
							}
							GlobalUnlock(hData);
						}

					}
				}
				uFormat = EnumClipboardFormats(uFormat);
			}
			CloseClipboard();
			result->Success(flutter::EncodableList(paths));
		}
		else {
			result->NotImplemented();
		}
	}

}  // namespace

void ReceiveSharingIntentPluginRegisterWithRegistrar(
	FlutterDesktopPluginRegistrarRef registrar) {
	ReceiveSharingIntentPlugin::RegisterWithRegistrar(
		flutter::PluginRegistrarManager::GetInstance()
		->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}
